import pandas as pd
from fastapi import UploadFile
from config.oss_config import bucket
from config.settings import OSS_CONFIG
from config.database import get_db
from utils.file_utils import get_file_type, generate_uuid, generate_oss_path
from dao.file_dao import FileDao
from utils.exceptions import NotFoundException, PermissionException, ParamException
from models.models import BatchProcesses, FileUploadsBatch
import io
import docx
import pdfplumber

class FileService:
    @staticmethod
    async def upload_files(files: list[UploadFile], sort_prompt: str, user_id: int):
        """
        上传文件并进行处理
        
        Args:
            files (list[UploadFile]): 要上传的文件列表
            sort_prompt (str): 排序提示词
            user_id (int): 用户ID
            
        Returns:
            dict: 包含批次ID和上传状态的字典
                - batch_id (str): 批次ID
                - message (str): 上传状态信息
                
        处理说明:
            1. 文本类型文件(type=1)包括：txt, md, doc, docx, pdf, csv, xls, xlsx, xlsm
               - 会保存原始文件
               - 同时提取文本内容保存为txt文件
            2. 其他类型文件：直接保存，不做处理
        """
        batch_id = generate_uuid()
        db = next(get_db())
        
        # 创建批次记录
        FileDao.create_batch(db, batch_id, sort_prompt, user_id=user_id)
        
        for file in files:
            file_type = get_file_type(file.filename)
            oss_path = generate_oss_path(file.filename)
            
            # 处理文本类文件
            if file_type == 1:
                content = await file.read()
                file_ext = file.filename.lower()
                original_filename = file.filename
                
                # 先保存原始文件
                original_oss_path = generate_oss_path(original_filename)
                bucket.put_object(original_oss_path, content)
                file_original_url = f"https://{OSS_CONFIG['bucket_name']}.{OSS_CONFIG['endpoint']}/{original_oss_path}"
                
                # 处理文件内容
                if file_ext.endswith('.csv'):
                    df = pd.read_csv(io.BytesIO(content))
                    content = df.to_string().encode()
                elif file_ext.endswith(('.xls', '.xlsx', '.xlsm')):
                    df = pd.read_excel(io.BytesIO(content))
                    content = df.to_string().encode()
                elif file_ext.endswith(('.txt', '.md')):
                    # 文本文件直接使用原始内容
                    if isinstance(content, str):
                        content = content.encode()
                elif file_ext.endswith(('.doc', '.docx')):
                    # 处理 Word 文档
                    doc = docx.Document(io.BytesIO(content))
                    text_content = []
                    for para in doc.paragraphs:
                        text_content.append(para.text)
                    content = '\n'.join(text_content).encode()
                elif file_ext.endswith('.pdf'):
                    # 处理 PDF 文档，使用pdfplumber提供更准确的文本提取
                    with pdfplumber.open(io.BytesIO(content)) as pdf:
                        text_content = []
                        for page in pdf.pages:
                            # 获取页面布局信息
                            words = page.extract_words(
                                x_tolerance=1.5,      # 减小水平容差，提高字符分组准确性
                                y_tolerance=3,        # 垂直容差，用于确定行间距
                                keep_blank_chars=False,
                                use_text_flow=True,   # 使用文本流模式，更好处理复杂布局
                                horizontal_ltr=True,  # 从左到右处理文本
                                vertical_ttb=True,    # 从上到下处理文本
                                extra_attrs=['size', 'top', 'bottom']  # 提取额外的布局信息
                            )
                            
                            if not words:
                                continue
                                
                            # 按垂直位置分组文字
                            current_line = []
                            current_top = words[0]['top']
                            line_height = words[0]['bottom'] - words[0]['top']
                            
                            for word in words:
                                # 如果新单词的顶部位置与当前行差距超过行高的一半，认为是新的一行
                                if abs(word['top'] - current_top) > line_height * 0.5:
                                    # 完成当前行，添加到结果中
                                    if current_line:
                                        text_content.append(' '.join(current_line))
                                    current_line = []
                                    current_top = word['top']
                                
                                current_line.append(word['text'])
                            
                            # 添加最后一行
                            if current_line:
                                text_content.append(' '.join(current_line))
                            
                            # 在页面之间添加分隔符
                            text_content.append('\n')
                    
                    # 合并所有文本，确保使用统一的换行符
                    content = '\n'.join(text_content).strip().encode('utf-8')
                
                # 保存处理后的文本文件
                filename_without_ext = original_filename.rsplit('.', 1)[0]
                oss_path = generate_oss_path(f"{filename_without_ext}.txt")
                bucket.put_object(oss_path, content)
                file_url = f"https://{OSS_CONFIG['bucket_name']}.{OSS_CONFIG['endpoint']}/{oss_path}"
            else:
                # 处理其他类型文件
                oss_path = generate_oss_path(file.filename)
                content = await file.read()
                bucket.put_object(oss_path, content)
                file_url = f"https://{OSS_CONFIG['bucket_name']}.{OSS_CONFIG['endpoint']}/{oss_path}"
                file_original_url = file_url  # 非文本文件，原始URL与处理后URL相同
            
            # 创建文件处理记录
            FileDao.create_process(db, batch_id, file.filename, file_url, file_type, file_original_url)
        
        return {"batch_id": batch_id, "message": "Files uploaded successfully"} 

    @staticmethod
    def get_user_batches(user_id: int, page_num: int = 1, page_size: int = 10):
        """
        获取用户的批次列表
        
        Args:
            user_id (int): 用户ID
            page_num (int, optional): 当前页码，从1开始. Defaults to 1
            page_size (int, optional): 每页显示的记录数. Defaults to 10
            
        Returns:
            dict: 包含批次列表和分页信息的字典
                - items (list): 批次列表
                - total (int): 总记录数
                - page (int): 当前页码
                - size (int): 每页大小
                - pages (int): 总页数
        """
        db = next(get_db())
        return FileDao.get_user_batches(
            db=db,
            user_id=user_id,
            page_num=page_num,
            page_size=page_size
        )

    @staticmethod
    def get_batch_processes(batch_id: str, user_id: int):
        """
        获取指定批次的文件处理记录
        
        Args:
            batch_id (str): 批次ID
            user_id (int): 用户ID，用于权限验证
            
        Returns:
            dict: 包含批次信息和处理记录的字典
                - batch_id (str): 批次ID
                - processes (list): 处理记录列表，每条记录包含：
                    - file_name (str): 文件名
                    - file_url (str): 处理后的文件URL
                    - file_original_url (str): 原始文件URL
                    - status (int): 处理状态
                    - type (int): 文件类型
                    
        Raises:
            NotFoundException: 批次不存在时抛出
            PermissionException: 用户无权访问该批次时抛出
        """
        db = next(get_db())
        batch = FileDao.get_batch_by_id(db, batch_id)
        if not batch:
            raise NotFoundException("批次不存在")
            
        if batch.user_id != user_id:
            raise PermissionException("无权查看此批次")
            
        processes = FileDao.get_batch_processes(db, batch_id)
        return {
            "batch_id": batch_id,
            "processes": processes
        } 

    @staticmethod
    def update_batch_status(batch_id: str, user_id: int) -> dict:
        """
        更新批次状态
        
        Args:
            batch_id (str): 批次ID
            user_id (int): 用户ID，用于权限验证
            
        Returns:
            dict: 更新后的批次信息
                - batch_id (str): 批次ID
                - status (int): 更新后的状态
                
        Raises:
            NotFoundException: 批次不存在时抛出
            PermissionException: 用户无权操作该批次时抛出
            ParamException: 批次状态不正确时抛出
        """
        db = next(get_db())
        try:
            # 使用 SELECT FOR UPDATE 锁定行，防止并发更新
            batch = db.query(FileUploadsBatch)\
                .filter(FileUploadsBatch.batch_id == batch_id)\
                .with_for_update()\
                .first()
            
            if not batch:
                raise NotFoundException("批次不存在")
                
            if batch.user_id != user_id:
                raise PermissionException("无权操作此批次")
                
            if batch.status != 0:
                raise ParamException("批次状态不正确，仅能操作待处理的批次")
            
            # 在同一个事务中更新批次和文件状态
            batch.status = 1
            db.query(BatchProcesses)\
                .filter(BatchProcesses.batch_id == batch_id)\
                .update({"status": 1})
            
            # 提交事务
            db.commit()
            
            return {
                "batch_id": batch.batch_id,
                "status": batch.status
            }
            
        except Exception as e:
            # 发生异常时回滚事务
            db.rollback()
            raise e
        finally:
            # 确保关闭数据库连接
            db.close() 